#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/msi.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include "drv_do.h"

#define DO_CMD_ID_MASK          0x00FF0000      /* DO 指令 ID 掩码 */
#define DO_CMD_CODE_MASK        0x0000FFFF      /* DO 指令编码掩码 */
#define DO_CMD_ID_SHIFT         16              /* DO 指令 ID 偏移 */
#define DO_CMD_CODE_SHIFT       0               /* DO 指令编码偏移 */

#define DO_DEF_LEVEL            0x00000000      /* DO 默认电平 */

#define DO_TIMEOUT_MASK         0x00FF0000      /* DO 吸合超时掩码 */
#define DO_TIMEOUT_STA_MASK     0x00000400      /* DO 吸合超时状态掩码 */
#define DO_MODE_MASK            0x00000300      /* DO 模式掩码 */
#define DO_TIMEOUT_SHIFT        16              /* DO 吸合超时偏移 */
#define DO_TIMEOUT_STA_SHIFT    10              /* DO 吸合超时状态偏移 */
#define DO_MODE_SHIFT           8               /* DO 模式偏移 */
#define DO_DEF_TIMEOUT_STA      DO_TIMEOUT_OFF  /* DO 默认吸合超时状态 */
#define DO_DEF_MODE             DO_MOD_LEVEL    /* DO 默认模式 */
#define DO_ID_ALL               0xFF            /* DO 所有通道*/

#define DO_PULSE_HIGH_MASK      0xFFFF0000      /* DO 高电平时间掩码 */
#define DO_PULSE_CYCLE_MASK     0x0000FFFF      /* DO 脉冲周期掩码 */
#define DO_PULSE_HIGH_SHIFT     16              /* DO 高电平时间偏移 */
#define DO_PULSE_CYCLE_SHIFT    0               /* DO 脉冲周期偏移 */
// #define DO_PULSE_CYCLE_MIN      20              /* DO 脉冲周期最小值 */
// #define DO_PULSE_CYCLE_MAX      20000           /* DO 脉冲周期最大值 */

/* DO 寄存器*/
enum do_reg_t {
    DO_VERSION_OFFSET           = 0x04,         /* 版本 寄存器 */
    DO_CMD_OFFSET               = 0x08,         /* DO 指令寄存器偏移 */
    DO_ALL_STA_OFFSET           = 0x34,         /* DO 所有通道状态寄存器偏移 */
    DO_READ_BACK_OFFSET         = 0x4C,         /* DO 所有通道回读寄存器偏移 */
    DO_MODE_OFFSET              = 0x54,         /* DO 模式寄存器偏移 */
    DO_ID_OFFSET                = 0x58,         /* DO 通道寄存器偏移 */
    DO_PULSE_NUM_OFFSET         = 0x5C,         /* DO 脉冲次数寄存器偏移 */
    DO_PULSE_PARAM_OFFSET       = 0x7C,         /* DO 脉冲高低时间寄存器偏移 */
    DO_STA_OFFSET               = 0xA8,         /* DO 通道状态寄存器偏移 */
};

/* 文件操作函数声明 */
static int open_do(struct inode *inode, struct file *filp);
static int release_do(struct inode *inode, struct file *filp);
static long ioctl_do(struct file *filp, unsigned int cmd, 
                     unsigned long arg);

/* 文件操作结构体 */
static const struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .open = open_do,
    .release = release_do,
    .read = NULL,
    .write = NULL,
    .unlocked_ioctl = ioctl_do,
    .poll = NULL,
    .llseek = no_llseek,
};

/* 读取 DO 寄存器 */
static u32 read_do_reg_le(struct pcie_dev_t *pcie_dev, u32 reg)
{
    u32 val = 0;
    unsigned long flags = 0;    /* 用于保存中断状态 */

    /* 获取自旋锁(保存中断状态) */
    spin_lock_irqsave(&pcie_dev->splock, flags);
    val = ioread32(pcie_dev->bar[0] + reg);
    /* 释放自旋锁(恢复中断状态) */
    spin_unlock_irqrestore(&pcie_dev->splock, flags);
    pr_debug("%s  reg=0x%08x, val=0x%08x", __func__, reg, val);

    return val;
}

/* 写入 DO 寄存器 */
static void write_do_reg_le(struct pcie_dev_t *pcie_dev, u32 reg, u32 val)
{
    unsigned long flags = 0;    /* 用于保存中断状态 */

    /* 获取自旋锁(保存中断状态) */
    spin_lock_irqsave(&pcie_dev->splock, flags);
    iowrite32(val, pcie_dev->bar[0] + reg);
    /* 释放自旋锁(恢复中断状态) */
    spin_unlock_irqrestore(&pcie_dev->splock, flags);
    pr_debug("%s reg=0x%08x, val=0x%08x", __func__, reg, val);
}

/* 配置所有 DO 电平 */
static void set_all_do_level(struct do_ctrl_t *pctrl, u32 level)
{
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return;
    }
    for (i = 0; i < pctrl->num; i++) {
        pctrl->gpo[i].set_level = ((level >> i) & 0x01);
    }
    pctrl->set_all_level = level;
    pctrl->write_reg(pctrl->pcie_dev, DO_ALL_STA_OFFSET, level);
    dev_dbg(pctrl->dev, "Set all DO level 0x%08x success", level);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);
}

/* 获取 DO 所有通道电平 */
static int get_all_do_level(struct do_ctrl_t *pctrl, u32 *level)
{
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    pctrl->get_all_level = pctrl->read_reg(pctrl->pcie_dev, DO_READ_BACK_OFFSET);
    for (i = 0; i < pctrl->num; i++) {
        pctrl->gpo[i].get_level = ((pctrl->get_all_level >> i) & 0x1);
    }
    dev_dbg(pctrl->dev, "Get all DO level 0x%08x success", pctrl->get_all_level);
    *level = pctrl->get_all_level;
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 配置 DO 模式 */
static int set_do_mode(struct do_ctrl_t *pctrl, u8 index, u8 mode)
{
    u8 mode_code = mode;
    u32 val = 0;
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    switch (mode) {
    case DO_MOD_LEVEL:
    case DO_MOD_PULSE:
        break;
    default:
        mode_code = DO_DEF_MODE;
        dev_warn(pctrl->dev, "DO mode %d not support, use default %d", mode, mode_code);
        break;
    }
    if (index >= pctrl->num) {
        for (i = 0; i< pctrl->num; i++) {
            pctrl->gpo[i].mode = mode_code;
        }
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, DO_ID_ALL);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_MODE_MASK);
        val |= (mode_code << DO_MODE_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set all DO mode %d success", mode_code);
    } else {
        pctrl->gpo[index].mode = mode_code;
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, index);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_MODE_MASK);
        val |= (mode_code << DO_MODE_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set DO%d mode %d success", index, mode_code);
    }
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 配置 DO 吸合超时状态 */
static int set_do_timeout_sta(struct do_ctrl_t *pctrl, u8 index, u8 sta)
{
    u8 sta_code = sta;
    u32 val = 0;
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    switch (sta) {
    case DO_TIMEOUT_OFF:
    case DO_TIMEOUT_ON:
        break;
    default:
        sta_code = DO_DEF_TIMEOUT_STA;
        dev_warn(pctrl->dev, "DO timeout sta %d not support, use default %d", sta, sta_code);
        break;
    }
    if (index >= pctrl->num) {
        for (i = 0; i< pctrl->num; i++) {
            pctrl->gpo[i].timeout_sta = sta_code;
        }
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, DO_ID_ALL);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_TIMEOUT_STA_MASK);
        val |= (sta_code << DO_TIMEOUT_STA_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set all DO timeout sta %d success", sta_code);
    } else {
        pctrl->gpo[index].timeout_sta = sta_code;
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, index);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_TIMEOUT_STA_MASK);
        val |= (sta_code << DO_TIMEOUT_STA_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set DO%d timeout sta %d success", index, sta_code);
    }
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 配置 DO 吸合超时时间 */
static int set_do_timeout(struct do_ctrl_t *pctrl, u8 index, u8 timeout)
{
    u32 val = 0;
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    if (index >= pctrl->num) {
        for (i = 0; i< pctrl->num; i++) {
            pctrl->gpo[i].timeout_sec = timeout;
        }
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, DO_ID_ALL);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_TIMEOUT_MASK);
        val |= (timeout << DO_TIMEOUT_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set all DO timeout %ds success", timeout);
    } else {
        pctrl->gpo[index].timeout_sec = timeout;
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, index);

        val = pctrl->read_reg(pctrl->pcie_dev, DO_MODE_OFFSET);
        val &= (~DO_TIMEOUT_MASK);
        val |= (timeout << DO_TIMEOUT_SHIFT);
        pctrl->write_reg(pctrl->pcie_dev, DO_MODE_OFFSET, val);
        dev_dbg(pctrl->dev, "Set DO%d timeout %ds success", index, timeout);
    }
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 配置 DO 电平 */
static int set_do_level(struct do_ctrl_t *pctrl, u8 index, u8 level)
{
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    if (index >= pctrl->num) {
        for (i = 0; i < pctrl->num; i++) {
            pctrl->gpo[i].set_level = level;
        }
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, DO_ID_ALL);
    } else {
        pctrl->gpo[index].set_level = level;
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, index);
    }
    pctrl->write_reg(pctrl->pcie_dev, DO_STA_OFFSET, level);
    dev_dbg(pctrl->dev, "Set DO%d level %d success", index, level);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 获取 DO 电平 */
static int get_do_level(struct do_ctrl_t *pctrl, u8 index, u8 *level)
{
    u8 i = 0;
    u8 id = (index % pctrl->num);

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    pctrl->get_all_level = pctrl->read_reg(pctrl->pcie_dev, DO_READ_BACK_OFFSET);
    for (i = 0; i < pctrl->num; i++) {
        pctrl->gpo[i].get_level = ((pctrl->get_all_level >> i) & 0x1);
    }
    dev_dbg(pctrl->dev, "Get DO%d level %d success", id, pctrl->gpo[id].get_level);
    *level = pctrl->gpo[id].get_level;
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 配置 DO 脉冲 */
static int set_do_pulse(struct do_ctrl_t *pctrl, u8 index, u16 high, u16 cycle, u32 num)
{
    // int ret = 0;
    u32 val = 0;
    u8 i = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    // if (cycle < DO_PULSE_CYCLE_MIN || cycle > DO_PULSE_CYCLE_MAX) {
    //     dev_err(pctrl->dev, "Set DO%d pulse failed, %d out of %d~%dus", 
    //             index, cycle, DO_PULSE_CYCLE_MIN, DO_PULSE_CYCLE_MAX);
    //     ret = -EFAULT;
    //     goto unlock;
    // }
    // if (high > (cycle / 2)) {
    //     dev_err(pctrl->dev, "Set DO%d pulse failed, %d/%d > 50%%", index, high, cycle);
    //     ret = -EFAULT;
    //     goto unlock;
    // }
    if (index >= pctrl->num) {
        for (i = 0; i < pctrl->num; i++) {
            pctrl->gpo[i].pulse.high = high;
            pctrl->gpo[i].pulse.cycle = cycle;
            pctrl->gpo[i].pulse.num = num;
        }
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, DO_ID_ALL);
    } else {
        pctrl->gpo[index].pulse.high = high;
        pctrl->gpo[index].pulse.cycle = cycle;
        pctrl->gpo[index].pulse.num = num;
        pctrl->write_reg(pctrl->pcie_dev, DO_ID_OFFSET, index);
    }
    val |= (high << DO_PULSE_HIGH_SHIFT);
    val |= (cycle << DO_PULSE_CYCLE_SHIFT);
    pctrl->write_reg(pctrl->pcie_dev, DO_PULSE_PARAM_OFFSET, val);
    pctrl->write_reg(pctrl->pcie_dev, DO_PULSE_NUM_OFFSET, num);
    dev_dbg(pctrl->dev, "Set DO%d pulse high %d cycle %d num %d success", 
            index, high, cycle, num);

// unlock:
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 开始 DO 脉冲 */
static int start_do_pulse(struct do_ctrl_t *pctrl, u8 index)
{
    u8 i = 0;
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    if (index >= pctrl->num) {
        for (i = 0; i < pctrl->num; i++) {
            pctrl->gpo[i].pulse_sta = 1;
        }
        val |= ((DO_ID_ALL << DO_CMD_ID_SHIFT) & DO_CMD_ID_MASK);
        val |= ((DO_CMD_START << DO_CMD_CODE_SHIFT) & DO_CMD_CODE_MASK);
        pctrl->write_reg(pctrl->pcie_dev, DO_CMD_OFFSET, val);
    } else {
        pctrl->gpo[index].pulse_sta = 1;
        val |= ((index << DO_CMD_ID_SHIFT) & DO_CMD_ID_MASK);
        val |= ((DO_CMD_START << DO_CMD_CODE_SHIFT) & DO_CMD_CODE_MASK);
        pctrl->write_reg(pctrl->pcie_dev, DO_CMD_OFFSET, val);
    }
    dev_dbg(pctrl->dev, "Start DO%d pulse success", index);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 停止 DO 脉冲 */
static int stop_do_pulse(struct do_ctrl_t *pctrl, u8 index)
{
    u8 i = 0;
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    if (index >= pctrl->num) {
        for (i = 0; i < pctrl->num; i++) {
            pctrl->gpo[i].pulse_sta = 0;
        }
        val |= ((DO_ID_ALL << DO_CMD_ID_SHIFT) & DO_CMD_ID_MASK);
        val |= ((DO_CMD_STOP << DO_CMD_CODE_SHIFT) & DO_CMD_CODE_MASK);
        pctrl->write_reg(pctrl->pcie_dev, DO_CMD_OFFSET, val);
    } else {
        pctrl->gpo[index].pulse_sta = 0;
        val |= ((index << DO_CMD_ID_SHIFT) & DO_CMD_ID_MASK);
        val |= ((DO_CMD_STOP << DO_CMD_CODE_SHIFT) & DO_CMD_CODE_MASK);
        pctrl->write_reg(pctrl->pcie_dev, DO_CMD_OFFSET, val);
    }
    dev_dbg(pctrl->dev, "Stop DO%d pulse success", index);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 打开设备 */
static int open_do(struct inode *inode, struct file *filp)
{
    /* 获取与 inode 关联的字符设备 */
    struct cdev_dev_t *cdev_dev = container_of(inode->i_cdev, struct cdev_dev_t, cdev);
    struct cdev_ctrl_t *cdev_ctrl = (struct cdev_ctrl_t *)(cdev_dev->parent);
    struct do_ctrl_t *pctrl = (struct do_ctrl_t *)(cdev_ctrl->parent);
    struct do_dev_t *priv = &pctrl->gpo[cdev_dev->index];

    /* 检查设备是否有效 */
    if (!priv || !priv->dev) {
        pr_err("Invalid parameters for %s", __func__);
        return -ENODEV;
    }
    /* 记录设备指针 */
    filp->private_data = priv;
    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&priv->lock)) {
        dev_err(priv->dev, "Acquire %s mutex failed", cdev_dev->name);
        return -ERESTARTSYS;
    }
    dev_dbg(priv->dev, "Open %s%d device", cdev_ctrl->name, cdev_dev->index);

    mutex_unlock(&priv->lock);

    return 0;
}

/* 关闭设备 */
static int release_do(struct inode *inode, struct file *filp)
{
    struct do_dev_t *priv = filp->private_data;

    if (priv) {
        mutex_lock(&priv->lock);
        dev_dbg(priv->dev, "Release %s device", priv->name);
        mutex_unlock(&priv->lock);
    }

    return 0;
}

/* IOCTL 处理 */
static long ioctl_do(struct file *filp, unsigned int cmd, 
                     unsigned long arg)
{
    struct do_dev_t *priv = filp->private_data;
    struct do_ctrl_t *pctrl = (struct do_ctrl_t *)(priv->parent);
    long ret = 0;
    u8 u8_val = 0;
    u32 u32_val = 0;
    struct pulse_t pulse = {0};

    /* 检查参数是否有效 */
    if (!priv || !priv->dev) {
        pr_err("Invalid parameters for %s", __func__);
        return -ENODEV;
    }

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&priv->lock)) {
        dev_err(priv->dev, "Acquire %s mutex failed", priv->name);
        return -ERESTARTSYS;
    }
    switch (cmd) {
    /* 配置 DO 所有通道电平 */
    case IOCTL_DO_SET_ALL_LEVEL:
        if (copy_from_user(&u32_val, (void __user *)arg, sizeof(u32_val))) {
            dev_err(priv->dev, "Set all DO level copy failed");
            ret = -EFAULT;
            goto unlock;
        }
        set_all_do_level(pctrl, u32_val);
        break;
    case IOCTL_DO_GET_ALL_LEVEL:
        ret = get_all_do_level(pctrl, &u32_val);
        if (ret < 0) {
            dev_err(priv->dev, "Get all DO level 0x%08x failed", u32_val);
            ret = -EFAULT;
            goto unlock;
        }
        if (copy_to_user((void __user *)arg, &u32_val, sizeof(u32_val))) {
            dev_err(priv->dev, "Get all DO level 0x%08x copy failed", u32_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 DO 模式 */
    case IOCTL_DO_SET_MODE:
        if (copy_from_user(&u8_val, (void __user *)arg, sizeof(u8_val))) {
            dev_err(priv->dev, "Set DO%d mode copy failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        if (set_do_mode(pctrl, priv->index, u8_val) < 0) {
            dev_err(priv->dev, "Set DO%d mode %d failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 DO 吸合状态 */
    case IOCTL_DO_SET_TIMEOUT_STA:
        if (copy_from_user(&u8_val, (void __user *)arg, sizeof(u8_val))) {
            dev_err(priv->dev, "Set DO%d timeout sta copy failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        if (set_do_timeout_sta(pctrl, priv->index, u8_val) < 0) {
            dev_err(priv->dev, "Set DO%d timeout sta %d failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 DO 吸合时间 */
    case IOCTL_DO_SET_TIMEOUT:
        if (copy_from_user(&u8_val, (void __user *)arg, sizeof(u8_val))) {
            dev_err(priv->dev, "Set DO%d timeout copy failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        if (set_do_timeout(pctrl, priv->index, u8_val) < 0) {
            dev_err(priv->dev, "Set DO%d timeout %ds failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 DO 电平 */
    case IOCTL_DO_SET_LEVEL:
        if (copy_from_user(&u8_val, (void __user *)arg, sizeof(u8_val))) {
            dev_err(priv->dev, "Set DO%d level copy failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        if (set_do_level(pctrl, priv->index, u8_val) < 0) {
            dev_err(priv->dev, "Set DO%d level %d failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取 DO 电平 */
    case IOCTL_DO_GET_LEVEL:
        ret = get_do_level(pctrl, priv->index, &u8_val);
        if (ret < 0) {
            dev_err(priv->dev, "Get DO%d level %d failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        if (copy_to_user((void __user *)arg, &u8_val, sizeof(u8_val))) {
            dev_err(priv->dev, "Get DO%d level %d copy failed", priv->index, u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 DO 脉冲 */
    case IOCTL_DO_SET_PULSE:
        if (copy_from_user(&pulse, (void __user *)arg, sizeof(pulse))) {
            dev_err(priv->dev, "Set DO%d pulse copy failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        if (set_do_pulse(pctrl, priv->index, pulse.high, pulse.cycle, pulse.num) < 0) {
            dev_err(priv->dev, "Set DO%d pulse high %d cycle %d num %d failed", 
                    priv->index, pulse.high, pulse.cycle, pulse.num);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 开始 DO 脉冲 */
    case IOCTL_DO_START_PULSE:
        if (start_do_pulse(pctrl, priv->index) < 0) {
            dev_err(priv->dev, "Start DO%d pulse failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 停止 DO 脉冲 */
    case IOCTL_DO_STOP_PULSE:
        if (stop_do_pulse(pctrl, priv->index) < 0) {
            dev_err(priv->dev, "Stop DO%d pulse failed", priv->index);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取驱动版本号 */
    case IOCTL_DO_GET_DRV_VERSION:
        if (copy_to_user((void __user *)arg, &pctrl->drv_ver, sizeof(pctrl->drv_ver))) {
            dev_err(pctrl->dev, "Get DO driver version %d copy failed", pctrl->drv_ver);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取 FPGA 版本号 */
    case IOCTL_DO_GET_FPGA_VERSION:
        if (copy_to_user((void __user *)arg, &pctrl->fpga_ver, sizeof(pctrl->fpga_ver))) {
            dev_err(pctrl->dev, "Get DO FPGA version %d copy failed", pctrl->fpga_ver);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    default:
        dev_err(priv->dev, "Ioctl DO%d cmd=0x%08x, dir=0x%X, size=%u, type=0x%02X('%c'), nr=%u not support\n",
                priv->index, cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd));
        ret = -ENOTTY;
        break;
    }

unlock:
    mutex_unlock(&priv->lock);

    return ret;
}

/* 初始化 DO 设备 */
static int init_do_dev(void *parent, struct device *dev, const char *name, 
                       struct do_dev_t *gpo, u8 index)
{
    int ret = 0;

    gpo->parent = parent;
    gpo->dev = dev;
    /* 初始化互斥锁 */
    mutex_init(&gpo->lock);
    strscpy(gpo->name, name, sizeof(gpo->name));
    gpo->index = index;
    gpo->mode = 0;
    gpo->set_level = 0;
    gpo->get_level = 0;
    gpo->pulse.high = 0;
    gpo->pulse.cycle = 0;
    gpo->pulse.num = 0;
    gpo->pulse_sta = 0;

    return ret;
}

/* 初始化 DO 控制器 */
int init_do_ctrl(struct pcie_dev_t *pcie_dev, struct cdev_dev_t *cdev_dev, 
                 struct do_ctrl_t *pctrl, struct do_dev_t *gpo, 
                 const char *ctrl_name, u8 dev_num)
{
    int ret = 0;
    int i = 0;
    char name[32];

    pctrl->dev = &pcie_dev->pdev->dev;
    /* 初始化互斥锁 */
    mutex_init(&pctrl->lock);
    snprintf(pctrl->name, sizeof(pctrl->name), "%s", ctrl_name);
    pctrl->num = dev_num;
    pctrl->pcie_dev = pcie_dev;
    pctrl->read_reg = read_do_reg_le;
    pctrl->write_reg = write_do_reg_le;
    pctrl->cdev_ctrl.cdev_dev = cdev_dev;
    pctrl->gpo = gpo;
    pctrl->drv_ver = CDEV_DRV_VERSION(DO_VER_MAJOR, DO_VER_MINOR, DO_VER_PATCH);
    pctrl->fpga_ver = pcie_dev->read_mem(pcie_dev->bar[0] + DO_VERSION_OFFSET);

    /* 初始化字符设备 */
    ret = init_cdev_ctrl(pctrl, &pctrl->cdev_ctrl, pctrl->name, dev_num);
    if (ret < 0) {
        dev_err(pctrl->dev, "Init %s controller failed", pctrl->name);
        goto err_free_dev;
    }
    for (i = 0; i < dev_num; i++) {
        snprintf(name, sizeof(name), "%s%d", pctrl->name, i);
        ret = init_cdev_dev(&pctrl->cdev_ctrl, pctrl->cdev_ctrl.cclass, 
                            &pctrl->cdev_ctrl.cdev_dev[i], &cdev_fops, pctrl->cdev_ctrl.major_devno, name, i);
        if (ret) {
            dev_err(pctrl->dev, "Init %s device failed", name);
            goto err_cleanup_cdev_ctrl;
        }
    }

    /* 初始化 DO 设备*/
    pctrl->set_all_level = 0;
    pctrl->get_all_level = 0;
    set_do_mode(pctrl, DO_ID_ALL, DO_DEF_MODE);      /* 配置 DO 模式 */
    set_all_do_level(pctrl, DO_DEF_LEVEL);           /* 配置所有 DO 电平 */
    for (i = 0; i < dev_num; i++) {
        init_do_dev(pctrl, pctrl->dev, pctrl->cdev_ctrl.cdev_dev[i].name, 
                    &pctrl->gpo[i], i);
    }

    return 0;

err_cleanup_cdev_ctrl:
    exit_cdev_ctrl(&pctrl->cdev_ctrl);
err_free_dev:

    return ret;
}

/* 注销 DO 控制器 */
void exit_do_ctrl(struct do_ctrl_t *pctrl, u8 dev_num)
{
    u8 i = 0;

    if (!pctrl) {
        pr_err("Invalid parameters for %s", __func__);
        return;
    }

    for (i = 0; i < dev_num; i++) {
        exit_cdev_dev(pctrl->cdev_ctrl.cclass, &pctrl->cdev_ctrl.cdev_dev[i]);
    }
    exit_cdev_ctrl(&pctrl->cdev_ctrl);
    dev_info(pctrl->dev, "Release cdev controller success");

    for (i = 0; i < dev_num; i++) {
        mutex_destroy(&pctrl->gpo[i].lock);
    }
    /* 注销互斥锁 */
    mutex_destroy(&pctrl->lock);

    dev_info(pctrl->dev, "PCIe character device %s removed", pctrl->name);
}
